package com.sqm.timecard;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;

/**
 * Main processor to analysis timecard file and absent file and output result file
 * 
 * @author qiming, initial version
 * @author qiming on February 22, 2007 for adding analysis absent file functionality 
 */
public class TimecardProcesser
{
    public static final String TIME_FORMAT="yyyy-MM-dd HH:mm:ss";
    public static final String DISPLAY_DATE_FORMAT="MM-d";
    public static final int BRIEF_CELL_NUMBER=9;
    public static final int DETAIL_CELL_NUMBER=4;
    private static final String VACATION_FILE_NAME="vacation_list.xls";
    private static HSSFCellStyle cellStyle = null;
    
    Collection<VacationRecord> vacationList;
    
    /**
     * Load original timecard record file
     * @param inputFileName
     * @return
     * @throws Exception
     */
    public OriginalTimecardList loadTimecardFile(String inputFileName) throws Exception
    {
        OriginalTimecardList ret = new OriginalTimecardList();
        FileInputStream fileIS=new FileInputStream(inputFileName);        
        HSSFWorkbook wb = new HSSFWorkbook(fileIS);        
        HSSFSheet sheet = wb.getSheetAt(0);
        
        int rowNumber = sheet.getLastRowNum();
        for( int i=1;i<=rowNumber;i++)
        {
            HSSFRow rowTemp = sheet.getRow(i);
            String employeeId = rowTemp.getCell((short) 1)
            .getStringCellValue();
            if (employeeId != null && employeeId.length() > 0)
            {
                Timecard tc = new Timecard(employeeId, rowTemp.getCell(
                        (short) 2).getStringCellValue(), (rowTemp.getCell(
                        (short) 3).getStringCellValue()).trim(), (rowTemp.getCell(
                                (short) 4).getStringCellValue()).trim(), (rowTemp.getCell(
                                        (short) 5).getStringCellValue()).trim());
                ret.addTimecard(employeeId, tc);
            }
        }
        fileIS.close();
        
        return ret;
    }
    
    /**
     * Load original vacation record file
     * @param fileName
     * @return
     * @throws Exception
     */
    public Collection<VacationRecord> loadVacationFile(String fileName) throws Exception
    {
        Collection<VacationRecord> ret = new ArrayList<VacationRecord>();
        FileInputStream fileIS=new FileInputStream(fileName);        
        HSSFWorkbook wb = new HSSFWorkbook(fileIS);        
        HSSFSheet sheet = wb.getSheetAt(0);
        
        int rowNumber = sheet.getLastRowNum();        
        for( int i=1;i<=rowNumber;i++)
        {
            HSSFRow rowTemp = sheet.getRow(i);
            String employeeId = (rowTemp.getCell((short) 1)
            .getNumericCellValue()+"").substring(0,4);
            if (employeeId != null && employeeId.length() > 0)
            {
                Date startDate = rowTemp.getCell(
                        (short) 2).getDateCellValue();
                Date endDate = rowTemp.getCell(
                        (short) 3).getDateCellValue();        
                String comments = "";
                Calendar startCal = Calendar.getInstance();
                Calendar endCal = Calendar.getInstance();
                startCal.setTime(startDate);
                endCal.setTime(endDate);
                int startDayOfYear = startCal.get(Calendar.DAY_OF_YEAR );
                int endDayOfYear = endCal.get(Calendar.DAY_OF_YEAR );   
                
                int vacationType=getVacationType(startCal,endCal);
                if( startDayOfYear == endDayOfYear)
                {
                    //This vacation is a whole day or less then one day 
                    VacationRecord tc = new VacationRecord(employeeId,
                            startCal, endCal, vacationType, comments);   
                    ret.add(tc);
                }
                else if (startDayOfYear < endDayOfYear)
                {
                    // This vacation is more than one day
                    Calendar tempStartCal = Config.adjustStandardTime((Calendar)startCal.clone(),"checkIn");
                    Calendar tempEndCal = Config.adjustStandardTime((Calendar)endCal.clone(),"takeOff");
                    int vacationNumber = endDayOfYear - startDayOfYear;

                    //set the first day of vacation
                    tempEndCal.set(Calendar.DAY_OF_YEAR, startDayOfYear);
                    vacationType = getVacationType(startCal, tempEndCal);
                    VacationRecord tc1 = new VacationRecord(employeeId,
                            startCal, tempEndCal, vacationType, comments);
                    ret.add(tc1);
                    
                    //set the middle days of vacation
                    for (int j = 1; j < vacationNumber; j++)
                    {
                        tempStartCal = (Calendar) tempStartCal.clone();
                        tempEndCal = (Calendar) tempEndCal.clone();
                        tempStartCal.set(Calendar.DAY_OF_YEAR, startDayOfYear
                                + j);
                        tempEndCal.set(Calendar.DAY_OF_YEAR, startDayOfYear
                                + j);
                        VacationRecord tc = new VacationRecord(employeeId,
                                tempStartCal, tempEndCal, VacationRecord.WHOLE_DAY_VACATION,
                                comments);
                        ret.add(tc);                        
                    }
                    
                    //set the last day of vacation
                    tempStartCal = (Calendar) tempStartCal.clone();
                    tempStartCal.set(Calendar.DAY_OF_YEAR, endDayOfYear);
                    vacationType = getVacationType(tempStartCal, endCal);
                    VacationRecord tc2 = new VacationRecord(employeeId,
                            tempStartCal, endCal, vacationType, comments);
                    ret.add(tc2);
                }
                else
                {
                    throw new Exception("Invalid Vacation!"+" Id="+employeeId+" startTime="+startDate);
                }
                    
            }
        }
        fileIS.close();
        
        return ret;
    }

    private static int getVacationType(Calendar startCal,Calendar endCal)
    {
        int startHour = startCal.get(Calendar.HOUR_OF_DAY);
        int endHour = endCal.get(Calendar.HOUR_OF_DAY);
        int checkInHour=Config.getInt("checkInTime.hour");   
        int takeOffHour = Config.getInt("takeOffTime.hour");

        if( startHour<=checkInHour )
        {
            if( endHour >=takeOffHour-1)
            {
                return VacationRecord.WHOLE_DAY_VACATION;
            }
            else if( endHour < takeOffHour-1)
            {
                return VacationRecord.MORNING_VACATION;
            }
        }
        else if( startHour > checkInHour )
        {
            if( endHour >=takeOffHour-1)
            {
                return VacationRecord.NOON_VACATION;
            }
            else if( endHour < takeOffHour-1)
            {
                return VacationRecord.MIDDLE_VACATION;
            }            
        }
        return 0;
    }
    
    /**
     * Output result to a excel file
     * 
     * @param outputFileName
     * @param reportList
     * @throws Exception
     */
    public void output(String outputFileName, Collection<PersonalMonthlyReport> reportList) throws Exception
    {
        HSSFWorkbook wb = new HSSFWorkbook();
        FileOutputStream fileOut = null;
        HSSFSheet sheetBrief = wb.createSheet("Brief");
        HSSFRow row = sheetBrief.createRow((short)0);
        
        HSSFCellStyle cellStyle = null;
        HSSFCellStyle title_CellStyle = null;
        HSSFFont font = null;
        
        //draw brief sheet
        for( int k = 0; k < BRIEF_CELL_NUMBER; k++ )
        {
            sheetBrief.setColumnWidth( ( short)k,( short)5000);
            HSSFCell cell = row.createCell(( short) k);
            if( title_CellStyle == null )
                title_CellStyle = wb.createCellStyle();
            if( font == null )
                font = wb.createFont();

            font.setFontHeightInPoints((short)12);
            font.setFontName("Courier New");

            title_CellStyle.setAlignment( HSSFCellStyle.ALIGN_CENTER );
            title_CellStyle.setFillForegroundColor(HSSFColor.BLUE.index);
            title_CellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
            title_CellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN );
            title_CellStyle.setBorderRight( HSSFCellStyle.BORDER_THIN);
            title_CellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN );
            title_CellStyle.setFont(font);
            cell.setCellStyle(title_CellStyle );
            if( k == 0 )
                cell.setCellValue( "Name" );
            else if( k==1 )
                cell.setCellValue( "Phone Number" );
            else if( k==2 )
                cell.setCellValue( "Late" );           
            else if( k==3 )
                cell.setCellValue( "Leave Early" );           
            else if( k==4 )
                cell.setCellValue( "Forget Record" );    
            else if( k==5 )
                cell.setCellValue( "Donative" );                
            else if( k==6 )
                cell.setCellValue( "Total WorkTime" );
            else if( k==7 )
                cell.setCellValue( "Avg WorkTime" );
            else if( k==8 )
                cell.setCellValue( "Normal Workday" );
            
        }
        
        int row_num=1;
        for( PersonalMonthlyReport report:reportList)
        {
            HSSFRow lineRow = sheetBrief.createRow((short)row_num);
            fillCell( wb,lineRow.createCell((short)0) , report.getEmployeeName() ,HSSFCell.ENCODING_UTF_16, 0 );
            fillCell( wb,lineRow.createCell((short)1) , report.getEmployeeId() ,HSSFCell.ENCODING_UTF_16, 0 );
            fillCell( wb,lineRow.createCell((short)2) , report.getStatItems(PersonalMonthlyReport.LATE)+"" ,HSSFCell.ENCODING_UTF_16, 0 );
            fillCell( wb,lineRow.createCell((short)3) , report.getStatItems(PersonalMonthlyReport.EARLY)+"" ,HSSFCell.ENCODING_UTF_16, 0 );
            fillCell( wb,lineRow.createCell((short)4) , report.getStatItems(PersonalMonthlyReport.FORGET)+"" ,HSSFCell.ENCODING_UTF_16, 0 );
            fillCell( wb,lineRow.createCell((short)5) , report.getDonative() ,HSSFCell.ENCODING_UTF_16, 0);
            fillCell( wb,lineRow.createCell((short)6) , report.getTotalWorkTimeByHour()+" Hours" ,HSSFCell.ENCODING_UTF_16, 0);
            fillCell( wb,lineRow.createCell((short)7) , String.valueOf(report.getAvgWorkTimePerDay())+" Hours" ,HSSFCell.ENCODING_UTF_16, 0);
            fillCell( wb,lineRow.createCell((short)8) , report.getValidWorkDayNumber()+"" ,HSSFCell.ENCODING_UTF_16, 0);

            row_num ++;
        }   
        
        //draw detailed sheet
        HSSFSheet sheetDetail = wb.createSheet("Detail");
        HSSFRow rowD = sheetDetail.createRow((short)0);          
        for( int k = 0; k < DETAIL_CELL_NUMBER; k++ )
        {
            sheetDetail.setColumnWidth( ( short)k,( short)5000);
            HSSFCell cell = rowD.createCell(( short) k);
            if( title_CellStyle == null )
                title_CellStyle = wb.createCellStyle();
            if( font == null )
                font = wb.createFont();

            font.setFontHeightInPoints((short)12);
            font.setFontName("Courier New");

            title_CellStyle.setAlignment( HSSFCellStyle.ALIGN_CENTER );
            title_CellStyle.setFillForegroundColor(HSSFColor.BLUE.index);
            title_CellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
            title_CellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN );
            title_CellStyle.setBorderRight( HSSFCellStyle.BORDER_THIN);
            title_CellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN );
            title_CellStyle.setFont(font);
            cell.setCellStyle(title_CellStyle );
            if( k == 0 )
                cell.setCellValue( "Name" );
            else if( k==1 )
                cell.setCellValue( "Phone Number" );
            else if( k==2 )
                cell.setCellValue( "Check in time" );           
            else if( k==3 )
                cell.setCellValue( "Take off time" );                         
        }
        row_num=1;
        SimpleDateFormat sdf = new SimpleDateFormat(TimecardProcesser.TIME_FORMAT);
        for( PersonalMonthlyReport report:reportList)
        {
            Collection<DailyRecord> records = report.getDetailedRecords().values();
            for (DailyRecord record : records)
            {
                String checkInTime="";
                String takeOffTime="";
                int checkinColor = 0;
                int takeOffColor = 0;
                if( DailyRecord.STATUS_ABNORMAL.equals(record.getCheckinStatus() ) )
                { 
                    checkinColor=HSSFColor.RED.index;
                }
                if( DailyRecord.STATUS_ABNORMAL.equals(record.getTakeOffStatus() ) )
                {
                    takeOffColor=HSSFColor.RED.index;
                }
                if( DailyRecord.STATUS_VACATION.equals(record.getCheckinStatus() ) )
                { 
                    checkinColor=HSSFColor.BLUE.index;
                }
                if( DailyRecord.STATUS_VACATION.equals(record.getTakeOffStatus() ) )
                {
                    takeOffColor=HSSFColor.BLUE.index;
                }
                if( record.getCheckInTime() != null )
                {
                    checkInTime=sdf.format(record.getCheckInTime().getTime());
                }
                if( record.getTakeOffTime() != null )
                {
                    takeOffTime=sdf.format(record.getTakeOffTime().getTime());
                }
                HSSFRow lineRow = sheetDetail.createRow((short) row_num);
                fillCell(wb, lineRow.createCell((short) 0), report
                        .getEmployeeName(), HSSFCell.ENCODING_UTF_16, 0);
                fillCell(wb, lineRow.createCell((short) 1), report
                        .getEmployeeId(), HSSFCell.ENCODING_UTF_16, 0);
                fillCell(wb, lineRow.createCell((short) 2), checkInTime, HSSFCell.ENCODING_UTF_16, checkinColor);
                fillCell(wb, lineRow.createCell((short) 3), takeOffTime, HSSFCell.ENCODING_UTF_16, takeOffColor);
                row_num++;
            }
        }   
              
        
        fileOut = new FileOutputStream(outputFileName);
        wb.write(fileOut);
        fileOut.close();
    }
    /**
     * Creates a cell and aligns it a certain way.
     *
     * @param wb        the workbook
     * @param color TODO
     * @param row       the row to create the cell in
     * @param column    the column number to create the cell in
     * @param align     the alignment for the cell.
     */
    private void fillCell( HSSFWorkbook wb,HSSFCell cell, String value ,int encoding, int color)
    {
        if( color != 0  )
        {
//            if( cellStyle == null )
                cellStyle = wb.createCellStyle();
            cellStyle.setFillForegroundColor((short)color);
            cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
            //cellStyle.setAlignment( );
            cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
            cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN );
            cellStyle.setBorderRight( HSSFCellStyle.BORDER_THIN);
            cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN );
            cell.setCellStyle(cellStyle);
        }
        //cell.setEncoding( ( short)encoding );
        cell.setCellValue( value );
    }
    
    public Collection<PersonalMonthlyReport> process(OriginalTimecardList otList) throws Exception
    {
        Collection<PersonalMonthlyReport> ret = new ArrayList<PersonalMonthlyReport>();
        Collection<String> employeeIds = otList.getAllEmployeeIds();
        SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT);
        for( String eId:employeeIds)
        {
            PersonalMonthlyReport prep = fillInPersonalDailyRecord(otList, sdf, eId);            
            calculateStatInfo(prep);    
            ret.add(prep);
        }
        
        return ret;
    }

    private PersonalMonthlyReport fillInPersonalDailyRecord(OriginalTimecardList otList, SimpleDateFormat sdf, String eId) throws ParseException
    {
        Collection<Timecard> tcs = otList.getTimecards(eId);
        PersonalMonthlyReport prep= new PersonalMonthlyReport(eId,Config.getProperty(eId));
        for(Timecard tc:tcs)
        {               
            Date date = sdf.parse(tc.getRecordTime());
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            int dayOfYear = cal.get(Calendar.DAY_OF_YEAR);

            if( !isWeekday(cal) )
            {
                //It's weekend day
                continue;
            }
            DailyRecord record = prep.getDailyRecordByDate(dayOfYear);            
            if( record == null)
            {
                record = new DailyRecord(dayOfYear);
                prep.addDailyRecord(dayOfYear, record);                    
            }
            
            if( Timecard.TYPE_CHECKIN.equalsIgnoreCase( tc.getType()) )
            {
                if( record.getCheckInTime() == null )
                    record.setCheckInTime(cal);
                else
                    record.setTakeOffTime(cal);
            }
            else if( Timecard.TYPE_TAKEOFF.equalsIgnoreCase( tc.getType()))
            {
                record.setTakeOffTime(cal);
            }                
//            System.out.println(dayOfYear+":"+tc.getType());
        }
        return prep;
    }

    private VacationRecord findPersonalVacationRecord(Integer date, String employeeId,
            Collection<VacationRecord> vacationList)
    {
        VacationRecord ret = null;
        for( VacationRecord vr:(Collection<VacationRecord>)vacationList)
        {
            int tempDate = vr.getStartTime().get(Calendar.DAY_OF_YEAR);
            if( employeeId.equalsIgnoreCase(vr.getEmployeeId()) && date==tempDate)
            {
                ret = vr;
                break;
            }
        }
        return ret;
    }
    
    /*
     * Main method to calculate the time card summary info by person
     */
    private void calculateStatInfo(PersonalMonthlyReport prep) throws Exception
    {
        //analyze someone's all records
        int year = Config.getInt("Year");
        int monthOfYear = (Config.getInt("monthOfYear")-1);
        int startDateOfMonth=Config.getInt("startDateOfMonth");
        int endDateOfMonth=Config.getInt("endDateOfMonth");
        
        //retrieve standard check in and take off time
        int checkInHour=Config.getInt("checkInTime.hour");   
        int checkInMinute=Config.getInt("checkInTime.minute");
        int takeOffHour = Config.getInt("takeOffTime.hour");
        int takeOffMinute = Config.getInt("takeOffTime.minute");
        int overtimeHour = Config.getInt("overtime.hour");
        int overtimeMinute = Config.getInt("overtime.minute");
        
        //find the first date and the end date of this month
        Calendar startCal= Calendar.getInstance();
        startCal.set(Calendar.YEAR, year);
        startCal.set(Calendar.MONTH, monthOfYear);
        startCal.set(Calendar.DAY_OF_MONTH, startDateOfMonth);
        startCal.set(Calendar.HOUR_OF_DAY, 0);
        startCal.set(Calendar.MINUTE, 0);
        startCal.set(Calendar.SECOND, 0);
        startCal.set(Calendar.MILLISECOND, 0);
        Calendar endCal= Calendar.getInstance();
        endCal.set(Calendar.YEAR, year);
        endCal.set(Calendar.MONTH, monthOfYear+1);
        endCal.set(Calendar.DAY_OF_MONTH, endDateOfMonth+1);
        endCal.set(Calendar.HOUR_OF_DAY,0);
        endCal.set(Calendar.MINUTE, 0);
        endCal.set(Calendar.SECOND, 0);
        endCal.set(Calendar.MILLISECOND, 0);
        
        //Load vacation records
        if (vacationList == null)
        	vacationList = loadVacationFile(VACATION_FILE_NAME);        
        
        Calendar tempCal = (Calendar)startCal.clone();
        Calendar previousTakeOffTime= null;
        while( tempCal.before(endCal))
        {            
            if( !isWeekday(tempCal) )
            {
                //It's weekend day,
                tempCal.add(Calendar.DAY_OF_YEAR,1);
                continue;
            }
            
            int date = tempCal.get(Calendar.DAY_OF_YEAR);
            VacationRecord vr = findPersonalVacationRecord(date,prep.getEmployeeId(),vacationList);
            
//            System.out.println("vr="+vr);
            DailyRecord tempRec = prep.getDailyRecordByDate(date);
            if( tempRec == null)
            {                
                //There are no records this day, count 2 forget times or vacation
                tempRec = new DailyRecord(date);
                prep.addDailyRecord(date, tempRec);
                if( vr!=null && vr.getVacationType()==VacationRecord.WHOLE_DAY_VACATION )
                {
                    tempRec.setCheckinStatus(DailyRecord.STATUS_VACATION);
                    tempRec.setTakeOffStatus(DailyRecord.STATUS_VACATION);
                    prep.recordStatDetailedDays(PersonalMonthlyReport.VACATION,
                            (Calendar) tempCal.clone());
                }
                else
                {
                    tempRec.setCheckinStatus(DailyRecord.STATUS_ABNORMAL);
                    tempRec.setTakeOffStatus(DailyRecord.STATUS_ABNORMAL);
                    prep.increaseTimes(PersonalMonthlyReport.FORGET, 2);
                    prep.recordStatDetailedDays(PersonalMonthlyReport.FORGET,
                            (Calendar) tempCal.clone());
                }
            }
            else
            {
                //process check in time
                if( tempRec.getCheckInTime() == null )
                {
                    if( vr!=null && (vr.getVacationType()==VacationRecord.MORNING_VACATION  || vr.getVacationType()==VacationRecord.WHOLE_DAY_VACATION ))
                    {
                        tempRec.setCheckinStatus(DailyRecord.STATUS_VACATION);
                        prep.recordStatDetailedDays(PersonalMonthlyReport.VACATION,
                                (Calendar) tempCal.clone());
                    }
                    else
                    {
                        tempRec.setCheckinStatus(DailyRecord.STATUS_ABNORMAL);
                        prep.increaseTimes(PersonalMonthlyReport.FORGET, 1);
                        prep.recordStatDetailedDays(
                                PersonalMonthlyReport.FORGET,
                                (Calendar) tempCal.clone());
                    }
                }
                else 
                {
                	//has check in time
                    Calendar standardCheckInTime=(Calendar)tempRec.getCheckInTime().clone();
                    standardCheckInTime.set(Calendar.HOUR_OF_DAY,checkInHour);
                    standardCheckInTime.set(Calendar.MINUTE,checkInMinute);
                    standardCheckInTime.set(Calendar.SECOND,0);
                    if( standardCheckInTime.before( tempRec.getCheckInTime() ))
                    {
                        //Means late or vacation
                        if( vr!=null && (vr.getVacationType()==VacationRecord.MORNING_VACATION || vr.getVacationType()==VacationRecord.WHOLE_DAY_VACATION))
                        {
                            tempRec.setCheckinStatus(DailyRecord.STATUS_VACATION);
                            prep.recordStatDetailedDays(PersonalMonthlyReport.VACATION,
                                    (Calendar) tempCal.clone());
                        }
                        else
                        {
                            int previousTakeOffHour = 19;
                            if( previousTakeOffTime!=null)
                            {
                                previousTakeOffHour = previousTakeOffTime.get(Calendar.HOUR_OF_DAY);
                            }
                            if (previousTakeOffHour >= 23)
                            {
                                // The take off time of previous day later than
                                // 23, so can late today.
                            }
                            else
                            {
                                prep.increaseTimes(PersonalMonthlyReport.LATE,
                                        1);
                                tempRec
                                        .setCheckinStatus(DailyRecord.STATUS_ABNORMAL);
                                prep.recordStatDetailedDays(
                                        PersonalMonthlyReport.LATE,
                                        (Calendar) tempCal.clone());
                            }
                        }
                    }               
                }
                
                //process take off time
                if( tempRec.getTakeOffTime() == null )
                {
                    if( vr!=null && (vr.getVacationType()==VacationRecord.NOON_VACATION|| vr.getVacationType()==VacationRecord.WHOLE_DAY_VACATION) )
                    {
                        tempRec.setTakeOffStatus(DailyRecord.STATUS_VACATION);
                        prep.recordStatDetailedDays(PersonalMonthlyReport.VACATION,
                                (Calendar) tempCal.clone());
                    }
                    else
                    {
                        tempRec.setTakeOffStatus(DailyRecord.STATUS_ABNORMAL);
                        prep.increaseTimes(PersonalMonthlyReport.FORGET, 1);
                        prep.recordStatDetailedDays(
                                PersonalMonthlyReport.FORGET,
                                (Calendar) tempCal.clone());
                    }
                }
                else 
                {
                	//has take off time
                    Calendar standardTakeOffTime=(Calendar)tempRec.getTakeOffTime().clone();
                    previousTakeOffTime = (Calendar)tempRec.getTakeOffTime().clone();
                    standardTakeOffTime.set(Calendar.HOUR_OF_DAY,takeOffHour);
                    standardTakeOffTime.set(Calendar.MINUTE,takeOffMinute);
                    standardTakeOffTime.set(Calendar.SECOND,0);
                    
                    //calculate real work time for this day, minutes info
                    int workTimeMinutes=0;
					if (tempRec.getCheckInTime() != null
							&& tempRec.getTakeOffTime() != null)
					{
						// Normal workday, has both checkin and take off time
						int checkInTime = (int) tempRec.getCheckInTime()
								.getTimeInMillis();
						int takeOffTime = (int) tempRec.getTakeOffTime()
								.getTimeInMillis();

						workTimeMinutes = (int) (takeOffTime - checkInTime)
								/ (60 * 1000);

						/*
						 * fill the workDurationMinutes info for this month and
						 * calculate avg and total work time.
						 */
						prep.setTotalWorkTime(prep.getTotalWorkTime()
								+ workTimeMinutes);
						prep.setValidWorkDayNumber(prep.getValidWorkDayNumber() + 1);
					}
					else
					{
						//If this guy forgets to flash finger, just count 4 hours (half day) by default.
						//Fixed a bug here, if one guy has forgot one of the check in or take off, we shouldn't count the work time at all.
						/*prep.setTotalWorkTime(prep.getTotalWorkTime()
								+ (int)(4*60));
								*/
					}
						
                    
                    if( standardTakeOffTime.after( tempRec.getTakeOffTime() ))
                    {
                    	//leave earlier than standard take off time.
                    	
                        if (tempRec.getCheckInTime() != null)
                        {
                        	//has check in time and take off time.
                            int workDurationHour=Config.getInt("workDuration.Hour");
                            int workDurationMinute=Config.getInt("workDuration.minute");
                                                                                 
                            if (workTimeMinutes < (workDurationHour*60+workDurationMinute))
                            {
                                // Means take off early or vacation
                                if( vr!=null )
                                {
                                    tempRec.setTakeOffStatus(DailyRecord.STATUS_VACATION);
                                    prep.recordStatDetailedDays(PersonalMonthlyReport.VACATION,
                                            (Calendar) tempCal.clone());
                                }
                                else
                                {
                                    prep.increaseTimes(
                                            PersonalMonthlyReport.EARLY, 1);
                                    tempRec
                                            .setTakeOffStatus(DailyRecord.STATUS_ABNORMAL);
                                    prep.recordStatDetailedDays(
                                            PersonalMonthlyReport.EARLY,
                                            (Calendar) tempCal.clone());
                                }
                            }
                            
                        }
                    }     
                    else
                    {
                        //process overtime logic
                        Calendar standardOverTime=(Calendar)tempRec.getTakeOffTime().clone();
                        standardOverTime.set(Calendar.HOUR_OF_DAY,overtimeHour);
                        standardOverTime.set(Calendar.MINUTE,overtimeMinute);
                        standardOverTime.set(Calendar.SECOND,0);
                        if( tempRec.getTakeOffTime().after( standardOverTime ))
                        {
                            //Means work overtime
                            prep.increaseTimes(PersonalMonthlyReport.OVERTIME, 1);
                            prep.recordStatDetailedDays(PersonalMonthlyReport.OVERTIME,(Calendar)tempCal.clone());
                        }
                    }
                }
            }
            tempCal.add(Calendar.DAY_OF_YEAR,1);
        }
    }
    
    
    /**
     * Detect if the given date is weekday
     * @param time given date
     * @return true if it is weekday,false otherwise
     */
    private boolean isWeekday(Calendar time)
    {
        boolean ret = true;
        int dayOfWeek = time.get(Calendar.DAY_OF_WEEK);
        int dayOfYear = time.get(Calendar.DAY_OF_YEAR);
        
        String abnormalWorkdays=Config.getProperty("abnormalWeekdays");
        String commonVacation=Config.getProperty("commonVacation");        

        for(Calendar cal:getDaysFromStringArray(abnormalWorkdays))
        {
            //It's a abnormal workday
            if( dayOfYear==cal.get(Calendar.DAY_OF_YEAR) )
                return true;
        }        
        for(Calendar cal:getDaysFromStringArray(commonVacation))
        {
            //It's a common vacation
            if( dayOfYear==cal.get(Calendar.DAY_OF_YEAR) )
                return false;
        }   

        if(dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY)
        {
            ret = false;
        }
        
        return ret;
    }
    
    private Collection<Calendar> getDaysFromStringArray(String str)
    {
        Collection<Calendar> ret = new ArrayList<Calendar>();
        if( str==null || str.length() ==0)
            return ret;
        
        String[] array=str.split(",");        
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for( int i=0;i<array.length;i++)
        {
            if (array[i] != null && array[i].length() > 0)
            {
                Date date = null;
                try
                {
                    date = sdf.parse(array[i]);
                } catch (ParseException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                Calendar cal = Calendar.getInstance();
                cal.setTime(date);
                ret.add(cal);
            }
        }
        return ret;
    }  
    
    public static void main( String[] args) throws Exception
    {
        TimecardProcesser timePro=new TimecardProcesser();
        
        System.out.println("Loading orginal timecard information...");
        
        OriginalTimecardList otList= timePro.loadTimecardFile("stuff.xls");
        
        System.out.println("Loaded successfully.");
        System.out.println("Start ayalyzing...");
        
        if (args != null && args.length > 0)
        {
            if ("txt".equalsIgnoreCase(args[0]))
            {
                System.out.println("Generate Txt...");
                
                FileWriter fw = new FileWriter("resultTXT.txt");
                fw.write(otList.toString());
                fw.flush();
                fw.close();
            }
        }
        else
        {        
            Collection<PersonalMonthlyReport> ret = timePro.process(otList);
            timePro.output("result.xls",ret);
        }
    }
} 